package hos.kotlin

import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt

/**
 * <p>Title: CoordinateUtils </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @date : 2024-07-23 19:39
 * @version : 1.0
 */
object CoordinateUtils {

    @JvmStatic
    fun getEarthRadius(): Double {
        return "6378137.0".toDouble()
    }

    @JvmStatic
    fun getEccentricityRatio(): Double {
        return "0.00669342162296594323".toDouble()
    }

    @JvmStatic
    fun getPI(): Double {
        return "3.14159265358979324".toDouble()
    }

    @JvmStatic
    fun getPIX(): Double {
        return getPI() * 3000.0 / 180.0
    }


    /**
     * 百度坐标(bd09II)转火星坐标(GCJ02)
     * 百度——>谷歌、高德
     *
     * @param lat 百度坐标纬度
     * @param lon 百度坐标经度
     */
    @JvmStatic
    fun baiduToGcj02(lat: Double, lon: Double): DoubleArray {
        val pi: Double = getPIX()
        val x = lon - 0.0065
        val y = lat - 0.006
        val z = sqrt(x * x + y * y) - 0.00002 * sin(y * pi)
        val theta = atan2(y, x) - 0.000003 * cos(x * pi)
        val gg_lng = z * cos(theta)
        val gg_lat = z * sin(theta)
        return doubleArrayOf(gg_lat, gg_lng)
    }

    /**
     * 火星坐标系(GCJ02)转百度坐标系(bd09II)
     * 谷歌、高德——>百度
     *
     * @param gcjLat 火星坐标纬度
     * @param gcjLon 火星坐标经度
     */
    @JvmStatic
    fun gcj02ToBaidu(gcjLat: Double, gcjLon: Double): DoubleArray {
        val pi = getPIX()
        val z = sqrt(gcjLon * gcjLon + gcjLat * gcjLat) + 0.00002 * sin(gcjLat * pi)
        val theta = atan2(gcjLat, gcjLon) + 0.000003 * cos(gcjLon * pi)
        val lng = z * cos(theta) + 0.0065
        val lat = z * sin(theta) + 0.006
        return doubleArrayOf(lat, lng)
    }

    /**
     * 火星坐标系(GCJ02)转地球坐标系(WGS84)
     *
     * @param gcjLat 火星坐标纬度
     * @param gcjLon 火星坐标经度
     */
    @JvmStatic
    fun gcj02ToWgs84(gcjLat: Double, gcjLon: Double): DoubleArray {
        val map = transform(gcjLat, gcjLon)
        return doubleArrayOf(gcjLat * 2 - map[1], gcjLon * 2 - map[0])
    }

    /**
     * 地球坐标系(WGS - 84)转火星坐标系(GCJ)
     *
     * @param wgLat 地球坐标纬度
     * @param wgLon 地球坐标经度
     */
    @JvmStatic
    fun wgs84ToGcj02(wgLat: Double, wgLon: Double): DoubleArray {
        // 如果是国外坐标点，则直接返回传进来的坐标点
        if (outOfChina(wgLon, wgLat)) {
            return doubleArrayOf(wgLat, wgLon)
        }
        return transform(wgLat, wgLon)
    }

    @JvmStatic
    fun wgs84ToBaiDu(lat: Double, lon: Double): DoubleArray {
        val map = wgs84ToGcj02(lat, lon)
        return gcj02ToBaidu(map[0], map[1])
    }

    @JvmStatic
    fun baiduToWgs84(lat: Double, lon: Double): DoubleArray {
        val map = baiduToGcj02(lat, lon)
        return gcj02ToWgs84(map[0], map[1])
    }

    /*判断是否在国内，不在国内则不做偏移*/
    @JvmStatic
    fun outOfChina(lon: Double, lat: Double): Boolean {
        return !(lon > 72.004 && lon < 135.05 && lat > 3.86 && lat < 53.55)
    }

    /**
     * 地球坐标系(WGS - 84)转火星坐标系(GCJ)
     *
     * @param lat 地球坐标纬度
     * @param lon 地球坐标经度
     */
    @JvmStatic
    private fun transform(lat: Double, lon: Double): DoubleArray {
        val pi = getPI()
        val eccentricityRatio = getEccentricityRatio()
        val earthRadius = getEarthRadius()
        var dLat = transformLat(lon - 105.0, lat - 35.0, pi)
        var dLon = transformLon(lon - 105.0, lat - 35.0, pi)
        val radLat = lat / 180.0 * pi
        var magic = sin(radLat)
        magic = 1 - eccentricityRatio * magic * magic
        val sqrtMagic = sqrt(magic)
        dLat =
            (dLat * 180.0) / ((earthRadius * (1 - eccentricityRatio)) / (magic * sqrtMagic) * pi)
        dLon = (dLon * 180.0) / (earthRadius / sqrtMagic * cos(radLat) * pi)
        return doubleArrayOf(lat + dLat, lon + dLon)
    }

    @JvmStatic
    private fun transformLat(x: Double, y: Double, pi: Double): Double {
        var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(
            abs(x)
        )
        ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0
        ret += (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0
        ret += (160.0 * sin(y / 12.0 * pi) + 320 * sin(y * pi / 30.0)) * 2.0 / 3.0
        return ret
    }

    @JvmStatic
    private fun transformLon(x: Double, y: Double, pi: Double): Double {
        var ret =
            300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x))
        ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0
        ret += (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0
        ret += (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0
        return ret
    }
}